<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>MediaDevices</title>
</head>

<body>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
  </style>

  <div>
    <video id="video" class="picture-video" autoplay playsinline="{true}" style="width: 90vw; height: 90vh"></video>
  </div>
  <!-- <script src="https://cdn.jsdelivr.net/gh/xiangyuecn/Recorder@latest/recorder.mp3.min.js"></script> -->
  <script src="../JS资源/recorder.wav.min.js"></script>
  <script>
    // 想要获取一个最接近 1280x720 的相机分辨率
    // const constraints = {
    //   // audio: true,
    //   audio: {
    //     sampleRate: 44100, // 采样率
    //     channelCount: 2, // 声道
    //     volume: 2.0, // 音量
    //   },

    //   video: {
    //     width: { min: 1280 },
    //     height: { min: 720 },
    //   },
    // };

    // navigator.mediaDevices
    //   .getUserMedia(constraints)
    //   .then(function (mediaStream) {
    //     const video = document.querySelector('#video');
    //     console.dir(video);
    //     video.srcObject = mediaStream;
    //     video.onloadedmetadata = function (e) {
    //       video.play();
    //     };
    //   })
    //   .catch(function (err) {
    //     console.log(err.name + ': ' + err.message);
    //   }); // 总是在最后检查
  </script>


  <script>
    //简单控制台直接测试方法：在任意(无CSP限制)页面内加载Recorder，加载成功后再执行一次本代码立即会有效果，import("https://xiangyuecn.gitee.io/recorder/recorder.mp3.min.js").then(function(s){console.log("import ok")}).catch(function(e){console.error("import fail",e)})

    let rec;
    /**调用open打开录音请求好录音权限**/
    let recOpen = function (successCB) {
      //一般在显示出录音按钮或相关的录音界面时进行此方法调用，后面用户点击开始录音时就能畅通无阻了
      rec = Recorder({
        //本配置参数请参考下面的文档，有详细介绍
        type: 'wav',
        sampleRate: 16000,
        bitRate: 16, //mp3格式，指定采样率hz、比特率kbps，其他参数使用默认配置；注意：是数字的参数必须提供数字，不要用字符串；需要使用的type类型，需提前把格式支持文件加载进来，比如使用wav格式需要提前加载wav.js编码引擎
        onProcess: function (buffers, powerLevel, bufferDuration, bufferSampleRate, newBufferIdx, asyncEnd) {
          //录音实时回调，大约1秒调用12次本回调，buffers为开始到现在的所有录音pcm数据块(16位小端LE)
          //可实时绘制波形（extensions目录内的waveview.js、wavesurfer.view.js、frequency.histogram.view.js插件功能）
          //可利用extensions/sonic.js插件实时变速变调，此插件计算量巨大，onProcess需要返回true开启异步模式
          //可实时上传（发送）数据，配合Recorder.SampleData方法，将buffers中的新数据连续的转换成pcm上传，或使用mock方法将新数据连续的转码成其他格式上传，可以参考文档里面的：Demo片段列表 -> 实时转码并上传-通用版；基于本功能可以做到：实时转发数据、实时保存数据、实时语音识别（ASR）等
        },
      });

      //let dialog=createDelayDialog(); 我们可以选择性的弹一个对话框：为了防止移动端浏览器存在第三种情况：用户忽略，并且（或者国产系统UC系）浏览器没有任何回调，此处demo省略了弹窗的代码
      rec.open(
        function () {
          console.log(' ==============rec.open ');
          //打开麦克风授权获得相关资源
          //dialog&&dialog.Cancel(); 如果开启了弹框，此处需要取消
          //rec.start() 此处可以立即开始录音，但不建议这样编写，因为open是一个延迟漫长的操作，通过两次用户操作来分别调用open和start是推荐的最佳流程
          successCB?.();
        },
        function (msg, isUserNotAllow) {
          //用户拒绝未授权或不支持
          //dialog&&dialog.Cancel(); 如果开启了弹框，此处需要取消
          console.log((isUserNotAllow ? 'UserNotAllow，' : '') + '无法录音:' + msg);
        }
      );
    };

    /**开始录音**/
    function recStart() {
      //打开了录音后才能进行start、stop调用
      rec.start();
    }

    /**结束录音**/
    function recStop() {
      rec.stop(
        function (blob, duration) {
          console.log(blob, (window.URL || webkitURL).createObjectURL(blob), '时长:' + duration + 'ms');
          rec.close(); //释放录音资源，当然可以不释放，后面可以连续调用start；但不释放时系统或浏览器会一直提示在录音，最佳操作是录完就close掉
          rec = null;
          const file = new File([blob], '录音.wav', {
            type: blob.type,
          });

          // const a = document.createElement('a');
          // document.body.appendChild(a);
          // a.style.display = 'none';
          // const url = window.URL.createObjectURL(blob);
          // a.href = url;
          // a.download = 'wavs.wav';
          // a.click();
          // document.body.removeChild(a);
          // window.URL.revokeObjectURL(url);

          console.log(file);
          //已经拿到blob文件对象想干嘛就干嘛：立即播放、上传

          /*** 【立即播放例子】 ***/
          let audio = document.createElement('audio');
          audio.controls = true;
          document.body.appendChild(audio);
          //简单利用URL生成播放地址，注意不用了时需要revokeObjectURL，否则霸占内存
          audio.src = (window.URL || webkitURL).createObjectURL(blob);
          audio.play();
        },
        function (msg) {
          console.log('录音失败:' + msg);
          rec.close(); //可以通过stop方法的第3个参数来自动调用close
          rec = null;
        }
      );
    }

    // ==========================================================
    //我们可以选择性的弹一个对话框：为了防止移动端浏览器存在第三种情况：用户忽略，并且（或者国产系统UC系）浏览器没有任何回调
    /*伪代码：
function createDelayDialog(){
  if(Is Mobile){//只针对移动端
      return new Alert Dialog Component
          .Message("录音功能需要麦克风权限，请允许；如果未看到任何请求，请点击忽略~")
          .Button("忽略")
          .OnClick(function(){//明确是用户点击的按钮，此时代表浏览器没有发起任何权限请求
              //此处执行fail逻辑
              console.log("无法录音：权限请求被忽略");
          })
          .OnCancel(NOOP)//自动取消的对话框不需要任何处理
          .Delay(8000); //延迟8秒显示，这么久还没有操作基本可以判定浏览器有毛病
  };
};
*/

    //这里假设立即运行，只录5秒，录完后立即播放，本段代码copy到控制台内可直接运行
    // recOpen(function () {
    //   console.log('==============recOpen-run');
    //   recStart();
    //   setTimeout(recStop, 5000);
    // });
  </script>

  <script>
    class AudioRecorder {
      static _instance = null;
      _recorder = null;
      constructor(options) {
        this._recorder = Recorder(options);
      }
      static getAudioInstance(options) {
        console.log('_instance-this', this);
        console.log('_instance', this._instance);
        if (!this._instance) {
          return (this._instance = new AudioRecorder({
            type: 'wav',
            sampleRate: 16000,
            bitRate: 16,
            ...options,
          }));
        }
        return this._instance;
      }
      // 关闭释放录音资源，释放完成后会调用success()回调。如果正在录音或者stop调用未完成前调用了close将会强制终止当前录音。
      close(success) {
        this._recorder.close(success);
      }
      // 开始录音，需先调用open；未close之前可以反复进行调用开始新的录音。
      start(success, fail) {
        this._recorder.open(
          (ev) => {
            this._recorder.start();
            success?.(ev);
          },
          (msg, isUserNotAllow) => {
            fail?.(msg, isUserNotAllow);
          }
        );
      }
      // 结束录音并返回录音数据blob对象，不限于立即播放、上传
      // success(blob,duration)：blob：录音数据audio/mp3|wav...格式，duration：录音时长，单位毫秒
      // fail(errMsg)：录音出错回调
      // autoClose：false 可选，是否自动调用close，默认为false不调用
      stop(autoClose, closeSuccess) {
        return new Promise((resolve, reject) => {
          this._recorder.stop(
            (blob, duration) => {
              this.close(closeSuccess);
              resolve({ blob, duration });
            },
            (ev) => {
              reject(ev);
            },
            autoClose
          );
        });
      }
      // 关闭释放录音资源
      close(success) {
        this._recorder.close(success);
      }
      // 暂停录音。
      pause() {
        this._recorder.pause();
      }
      // 恢复继续录音。
      resume() {
        this._recorder.resume();
      }
      // 判断浏览器是否支持录音(属性访问器)
      get IsSupport() {
        return Recorder.Support();
      }
      // 检测是否有Recorder已调用过open打开了麦克风录音功能。
      get IsOpen() {
        return Recorder.IsOpen();
      }
    }

    // const getAudioInstance = AudioRecorder.getAudioInstance();
    // const getAudioInstance1 = AudioRecorder.getAudioInstance();

    // getAudioInstance.start();
    // setTimeout(async () => {
    //   const { blob, duration } = await getAudioInstance.stop();
    //   console.log(blob, (window.URL || webkitURL).createObjectURL(blob), '时长:' + duration + 'ms');
    //   let audio = document.createElement('audio');
    //   audio.controls = true;
    //   document.body.appendChild(audio);
    //   //简单利用URL生成播放地址，注意不用了时需要revokeObjectURL，否则霸占内存
    //   audio.src = (window.URL || webkitURL).createObjectURL(blob);
    //   audio.play();
    // }, 3_300);
    // setTimeout(() => {
    //   getAudioInstance.stop((blob, duration) => {
    //     console.log(blob, (window.URL || webkitURL).createObjectURL(blob), '时长:' + duration + 'ms');
    //     console.log('****', getAudioInstance.IsOpen);
    //   });
    // }, 5000);

    // function* funcc() {
    //   console.log('funcc');
    //   // let f = 3 * (yield 1 + 2);
    //   let f = 3 * (yield console.log('s', 999));
    //   console.log('f', f);
    //   let s = 3 * (yield f);
    //   console.log('s', s);
    //   // console.log(777);
    //   // let b = yield 2;
    //   // console.log('b', b);
    //   // console.log(999);
    //   // return 100;
    // }
    // let sad = funcc();
    // console.log(sad.next(111));
    // console.log(sad.next(222));
    // console.log(sad.next(333));
    // // console.log(sad.next(333));

    function one() {
      setTimeout(() => {
        console.log('one', 111);
        let data = '用户数据';
        iterator.next(data);
      }, 1000);
    }
    function two() {
      setTimeout(() => {
        console.log('two', 222);
        let data = '订单数据';
        iterator.next(data);
      }, 2000);
    }
    function three() {
      setTimeout(() => {
        console.log('tthreewo', 333);
        let data = '商品数据';
        iterator.next(data);
      }, 1000);
    }

    function* gen() {
      console.log('gen');
      let users = yield one();
      console.log('users', users);
      // let orders = yield two();
      // console.log('orders', orders);
      // let goods = yield three();
      // console.log('goods', goods);
    }

    let iterator = gen();
    iterator.next();
  </script>
</body>

</html>